package com.gandong8.trace.tool.util;


import com.gandong8.trace.tool.domain.ResponseData;
import com.gandong8.trace.tool.domain.RequestData;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;

import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

/**
 * http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/index.html
 */
public class HttpUtil {

    public static ResponseData doGet(RequestData requestData) throws Exception{
        HttpGet httpGet = new HttpGet(requestData.getUrl());
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(requestData.getSocketTimeout())
                .setConnectTimeout(requestData.getConnectTimeout())
                .setRedirectsEnabled(requestData.isRedirectsEnabled())
                .build();
        httpGet.setConfig(requestConfig);
        Map<String, String> headers = requestData.getHeaders();
        if(headers != null){
            headers.forEach(httpGet::setHeader);
        }
        CloseableHttpClient httpClient = requestData.isHttps() ?  (CloseableHttpClient) getClientNoSSL() :HttpClients.createDefault();
        CloseableHttpResponse response = httpClient.execute(httpGet);
        ResponseData responseData = new ResponseData();
        try {
            int status = response.getStatusLine().getStatusCode();
            responseData.setHttpStatusCode(status);
            HttpEntity entity = response.getEntity();
            if(entity != null) {
                responseData.setData(EntityUtils.toString(entity));
            }
            return responseData;
        } finally{
            httpClient.close();
        }
    }

    private static Charset getCharset() {
        return StandardCharsets.UTF_8;
    }

    public static ResponseData doPost(RequestData requestData) throws Exception{

        HttpPost httpPost = new HttpPost(requestData.getUrl());
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(requestData.getSocketTimeout())
                .setConnectTimeout(requestData.getConnectTimeout())
                .setRedirectsEnabled(requestData.isRedirectsEnabled())
                .build();
        httpPost.setConfig(requestConfig);
        if(requestData.getPostJson() != null) {
            StringEntity entity = new StringEntity(requestData.getPostJson(), getCharset());
            httpPost.setEntity(entity);
            httpPost.setHeader("Content-Type", "application/json");
        }
        if(requestData.getPostParams() != null) {
            ArrayList<NameValuePair> params = new ArrayList<>();
            requestData.getPostParams().forEach((key,value)-> params.add(new BasicNameValuePair(key, value)));
            httpPost.setEntity(new UrlEncodedFormEntity(params));
            httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
        }
        if(requestData.getHeaders() != null){
            requestData.getHeaders().forEach(httpPost::setHeader);
        }
        CloseableHttpClient httpClient = requestData.isHttps() ? (CloseableHttpClient) getClientNoSSL() :HttpClients.createDefault();
        CloseableHttpResponse response = httpClient.execute(httpPost);
        ResponseData responseData = new ResponseData();
        try {
            int status = response.getStatusLine().getStatusCode();
            responseData.setHttpStatusCode(status);
            HttpEntity entity = response.getEntity();
            if(entity != null) {
                responseData.setData(EntityUtils.toString(entity, getCharset()));
            }
            return responseData;
        } finally{
            httpClient.close();
        }
    }

    public  static TrustManager[] buildTrustManagers() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                    }
                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                    }
                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
        };
    }



    public static HttpClient getClientNoSSL() {
        try {
            /*
            X509TrustManager trustManager = new X509TrustManager() {
                @Override public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                @Override public void checkClientTrusted(X509Certificate[] xcs, String str) {}
                @Override public void checkServerTrusted(X509Certificate[] xcs, String str) {}
            };

             */
            SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
            ctx.init(null, buildTrustManagers(), null);
            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
            RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)
                    .setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM,AuthSchemes.DIGEST))
                    .setProxyPreferredAuthSchemes(Collections.singletonList(AuthSchemes.BASIC)).build();
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https",socketFactory).build();
            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            return HttpClients.custom().setConnectionManager(connectionManager)
                    .setDefaultRequestConfig(requestConfig).build();
        } catch (KeyManagementException | NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }



    /**
     * http://hc.apache.org/httpcomponents-client-ga/examples.html
     */
   public void httpGet() throws Exception {
       CloseableHttpClient httpclient = HttpClients.createDefault();
       try {
           HttpGet httpget = new HttpGet("http://httpbin.org/");
           System.out.println("Executing request " + httpget.getRequestLine());
           // Create a custom response handler
           ResponseHandler<String> responseHandler = response -> {
               int status = response.getStatusLine().getStatusCode();
               if (status >= 200 && status < 300) {
                   HttpEntity entity = response.getEntity();
                   return entity != null ? EntityUtils.toString(entity) : null;
               } else {
                   throw new ClientProtocolException("Unexpected response status: " + status);
               }
           };
           String responseBody = httpclient.execute(httpget, responseHandler);
           System.out.println("----------------------------------------");
           System.out.println(responseBody);
       } finally {
           httpclient.close();
       }
   }
}
